package com.viknix.threadpool.manager.server.controller.config;

import com.viknix.threadpool.manager.common.constant.Constants;
import com.viknix.threadpool.manager.common.model.PoolParameterInfo;
import com.viknix.threadpool.manager.common.result.ResultModel;
import com.viknix.threadpool.manager.config.publisher.ConfigChangePublisher;
import com.viknix.threadpool.manager.config.service.ConfigService;
import com.viknix.threadpool.manager.config.util.Md5ConfigUtil;
import com.viknix.threadpool.manager.server.dependence.monitor.event.ConfigDataChangeEvent;
import com.viknix.threadpool.manager.server.dependence.util.TimeUtils;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.net.URLDecoder;
import java.sql.Timestamp;
import java.util.Map;

/**
 * @Author: Dongqi
 * @Date: 2021/10/25 14:55
 * @Version 1.0
 * @Description: ConfigController
 */
@Validated
@RestController
@RequestMapping(Constants.SERVER_CONFIG_CONTROLLER_PATH)
public class ConfigController {

    @Autowired
    private ConfigService configService;

    /**
     * 通过客户端调用，返回该线程池的配置信息
     *
     * @param poolId    线程池id
     * @param item      项目id
     * @param namespace 命名空间
     * @return 该线程池的配置信息
     */
    @GetMapping
    public Object getParameterInfo(@RequestParam("poolId") @NotBlank(message = "poolId 不能为空") String poolId,
                                   @RequestParam("item") @NotBlank(message = "item 不能为空") String item,
                                   @RequestParam(value = "namespace") @NotBlank(message = "namespace 不能为空") String namespace) {

        return configService.getParameterInfo(poolId, item, namespace);
    }

    /**
     * 发布/更新配置
     *
     * @param parameterInfo 新的配置信息
     * @return 是否成功
     */
    @PostMapping
    public ResultModel<Boolean> publishParameterInfo(@RequestBody @Valid PoolParameterInfo parameterInfo) {
        String threadPoolId = parameterInfo.getThreadPoolId();
        String projectId = parameterInfo.getItem();
        String namespace = parameterInfo.getNamespace();
        final Timestamp time = TimeUtils.getCurrentTime();
        int result = configService.insertOrUpdate(parameterInfo);
        // 发布事件：将事件放到阻塞队列中
        ConfigChangePublisher
                .notifyConfigChange(new ConfigDataChangeEvent(false, threadPoolId, projectId, namespace, time.getTime()));
        return result == 0 ? ResultModel.fail("发布配置失败！") : ResultModel.success("发布配置成功！");
    }

    /**
     * 客户端长轮询接口
     *
     * @param request  请求对象
     * @param response 响应对象
     */
    @SneakyThrows
    @PostMapping("/listener")
    public void listener(HttpServletRequest request, HttpServletResponse response) {
        request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);

        // 获取客户端拼接的参数
        String probeModify = request.getParameter(Constants.LISTENING_CONFIGS);
        if (StringUtils.isBlank(probeModify)) {
            throw new IllegalArgumentException(">>> invalid probeModify");
        }
        probeModify = URLDecoder.decode(probeModify, Constants.ENCODE);

        // key：threadPoolId + projectId + namespace
        // value：md5
        Map<String, String> clientMd5Map;
        try {
            // 将 threadPoolId 和 projectId 和 namespace 拼接起来 作为key
            // 线程池配置的 md5 作为 value 放到 clientMd5Map 集合中
            clientMd5Map = Md5ConfigUtil.getClientMd5Map(probeModify);
        } catch (Throwable e) {
            throw new IllegalArgumentException(">>> invalid probeModify");
        }
        configService.doPollingConfig(request, response, clientMd5Map, probeModify.length());
    }
}
